/**
 * Copyright (c) 2010, iDA MediaFoundry
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * - Neither the name of iDA MediaFoundry nor the names of its contributors may
 *   be used to endorse or promote products derived from this software without
 *   specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
package be.idamf.cortizone.ioc.injection
{
	import be.idamf.cortizone.ioc.factory.IObjectFactory;
	
	import flash.utils.Dictionary;
	import flash.utils.describeType;
	
	/**
	 * [Inject] = [Inject(name)] > [Inject(type)]
	 * [Inject(name)]
	 * [Inject(name="")]
	 * [Inject(type)]
	 * [Inject(type="")]
	 * [Inject(alias="")]
	 */
	public final class Syringe
	{
		////////////////////////////////////////////////////////////////////////
		// Static

		public static const INJECT_METADATA:String = "Inject";
		public static const INJECT_ALIAS:String = "alias";
		public static const INJECT_NAME:String = "name";
		public static const INJECT_TYPE:String = "type";

		public static var factory:IObjectFactory;

		protected static const REFERENCES:Dictionary = new Dictionary(true);

		public static function inject(target:Object):void
		{
			var typeInfo:XML;
			var metaDataNode:XML;
			var prescription:Prescription;
			var injectionCompleteHandlers:Array = [];
			var handler:String;

			if (factory == null)
				throw new Error("No factory defined.");

			typeInfo = describeType(target);

			for each (metaDataNode in typeInfo..metadata)
			{
				if (metaDataNode.attribute("name") == INJECT_METADATA)
				{
					prescription = new Prescription(metaDataNode.parent());
					administer(target, prescription);
				}
			}
			
			if (target is IPostInjectionAware)
				IPostInjectionAware(target).postInjection();
		}
		
		public static function disconnect(bean:Object):void
		{
			var target:Object;
			var prescription:Prescription;
			
			if (REFERENCES[bean] == undefined)
				return;
			
			for (target in REFERENCES[bean])
			{
				prescription = REFERENCES[bean][target];
				target[prescription.property] = null;
			}
			
			delete REFERENCES[bean];
		}
		
		private static function administer(target:Object, prescription:Prescription):void
		{
			var object:Object;
			
			with (prescription)
			{
				if (key == "" && value == "")
				{
					object = factory.getObjectByName(property);
					
					if (object == null)
						object = factory.getObjectByType(type);
				}
				
				if (key == "" && value == INJECT_TYPE)
					object = factory.getObjectByType(type);
				
				if (key == INJECT_TYPE && value != "")
					object = factory.getObjectByType(value);
				
				if (key == "" && value == INJECT_NAME)
					object = factory.getObjectByName(property);
				
				if (key == INJECT_NAME && value != "")
					object = factory.getObjectByName(value);
				
				if (key == INJECT_ALIAS && value != "")
					object = factory.getObjectByClassAlias(value);
				
				if (object != null)
				{
					target[property] = object;
					
					if (REFERENCES[object] == undefined)
					{
						Syringe.inject(object);						
						REFERENCES[object] = new Dictionary(true);
					}
					
					REFERENCES[object][target] = prescription;
				}
			}
		}
		
		public function Syringe()
		{
			throw new Error("Static Class");
		}
	}
}

internal class Prescription
{
	public var property:String = "";
	public var type:String = "";
	public var key:String = "";
	public var value:String = "";
	
	public function Prescription(info:XML = null):void
	{
		if (info != null)
		{
			property = info.@name.toString();
			type = info.@type.toString();
			key = info.@key.toString();
			value = info.@value.toString();
		}
	}
}